/// Parts Borrowed from IDesign.net

using System;
using System.Threading;
using System.Collections.Generic;
using System.Diagnostics;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Reflection;
using PDO.Utilities;
using System.Text;

public static class EventsHelper
{
    delegate void AsyncFire(Delegate del, object[] args);

    static void InvokeDelegate(Delegate del, object[] args)
    {
        ISynchronizeInvoke synchronizer = del.Target as ISynchronizeInvoke;
        try
        {
            if (synchronizer != null)//Requires thread affinity
            {
                if (synchronizer.InvokeRequired)
                {
                    /// This sometimes throws if an event is fired and the subscriber is disposed
                    /// before the event is received. 
                    /// Currently we just swallow the exception            

                    synchronizer.Invoke(del, args);

                    return;
                }
            }

            //Not requiring thread afinity or invoke is not required

            del.DynamicInvoke(args);
        }
        catch (TargetInvocationException ex)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("An asyncronous event handler threw an unhandled error");
            sb.AppendLine(string.Format("Method: {0}", del.Method.Name));
            if (null == del.Target)
            {
                sb.AppendLine("Target is null");
            }
            else
            {
                sb.AppendLine(string.Format("Target: {0}", del.Target.ToString()));
            }

            if (ex.InnerException != null)
            {
                sb.AppendLine("InnerException:");
                sb.AppendLine(ex.InnerException.ToString());
            }
#if(DEBUG)
            System.Windows.MessageBox.Show(sb.ToString());
#endif
            Logger.WriteException(sb.ToString(), "EventsHelper", ex);
            throw ex;
        }
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static void UnsafeFire(Delegate del, params object[] args)
    {
        if (args.Length > 7)
        {
            Trace.TraceWarning("Too many parameters. Consider a structure to enable the use of the type-safe versions");
        }
        if (del == null)
        {
            return;
        }
        Delegate[] delegates = del.GetInvocationList();

        foreach (Delegate sink in delegates)
        {
            try
            {
                InvokeDelegate(sink, args);
            }


                // TODO: revisit re updated exception handling / logging
#if(DEBUG)
            catch(Exception ex)
            {
                throw (ex);
            }
#else
            catch
            {
            }
#endif
        }
    }
    [MethodImpl(MethodImplOptions.NoInlining)]
    public static void UnsafeFireInParallel(Delegate del, params object[] args)
    {
        if (args.Length > 7)
        {
            Trace.TraceWarning("Too many parameters. Consider a structure to enable the use of the type-safe versions");
        }
        if (del == null)
        {
            return;
        }
        Delegate[] delegates = del.GetInvocationList();

        List<WaitHandle> calls = new List<WaitHandle>(delegates.Length);
        AsyncFire asyncFire = InvokeDelegate;

        foreach (Delegate sink in delegates)
        {
            IAsyncResult asyncResult = asyncFire.BeginInvoke(sink, args, null, null);
            calls.Add(asyncResult.AsyncWaitHandle);
        }
        WaitHandle[] handles = calls.ToArray();
        WaitHandle.WaitAll(handles);
        Action<WaitHandle> close = delegate(WaitHandle handle)
                                   {
                                       handle.Close();
                                   };
        Array.ForEach(handles, close);
    }
    [MethodImpl(MethodImplOptions.NoInlining)]
    public static void UnsafeFireAsync(Delegate del, params object[] args)
    {
        if (args.Length > 7)
        {
            Trace.TraceWarning("Too many parameters. Consider a structure to enable the use of the type-safe versions");
        }
        if (del == null)
        {
            return;
        }
        Delegate[] delegates = del.GetInvocationList();
        AsyncFire asyncFire = InvokeDelegate;
        AsyncCallback cleanUp = delegate(IAsyncResult asyncResult)
                                {
                                    asyncResult.AsyncWaitHandle.Close();
                                };
        foreach (Delegate sink in delegates)
        {
            asyncFire.BeginInvoke(sink, args, cleanUp, null);
        }
    }
    public static void Fire(EventHandler del, object sender, EventArgs e)
    {
        UnsafeFire(del, sender, e);
    }
    public static void Fire<T>(EventHandler<T> del, object sender, T t) where T : EventArgs
    {
        UnsafeFire(del, sender, t);
    }
  

    public static void FireInParallel(EventHandler del, object sender, EventArgs e)
    {
        UnsafeFireInParallel(del, sender, e);
    }
    public static void FireInParallel<T>(EventHandler<T> del, object sender, T t) where T : EventArgs
    {
        UnsafeFireInParallel(del, sender, t);
    }   

    public static void FireAsync(EventHandler del, object sender, EventArgs e)
    {
        UnsafeFireAsync(del, sender, e);
    }
    public static void FireAsync<T>(EventHandler<T> del, object sender, T t) where T : EventArgs
    {
        UnsafeFireAsync(del, sender, t);
    }

    public static void Fire(EventHandler eventHandler, object sender)
    {
        Fire(eventHandler, sender, EventArgs.Empty);
    }
}